---
title: 'Resource scheduler component'
description: 'Learn how to use the Schedule-X resource scheduler.'
---

# Resource Scheduler

A view for displaying resources (people, rooms, equipment etc.) in a time grid.

import {Callout} from "nextra/components";

<Callout type="info">
  This is a premium feature which requires an active license to be used. Learn more at [Schedule-X premium](/premium).
</Callout>

## Features & demo
import FeaturesList from "../../../../components/partials/features-list/features-list";

<FeaturesList features={[
  'Infinite scroll & lazy loading',
  'Drag & drop',
  'Resizing events',
  'Interactive modal for adding, editing or deleting events',
  'Outlook-inspired Scheduling assistant'
]} />

[Go to Demo](/demos/resource-scheduler)

<br />

<video autoPlay loop playsInline muted id={'demo'} className="landingPageDemoVideo" width={800} height={250}>
  <source src={'https://d19hgxvhjb2new.cloudfront.net/website/resource-demo.mp4'} type={'video/mp4'} />
</video>

## 2. Installation

### 2.1 Set up premium auth (only once)

Follow the [instructions for setting up an `.npmrc`](/docs/calendar/installing-premium)


### 2.2 Install


```bash copy
npm install @sx-premium/resource-scheduler @schedule-x/calendar @schedule-x/translations @schedule-x/theme-default
```

## Usage

```js
import { createCalendar } from '@schedule-x/calendar'
import { createEventsServicePlugin } from "@schedule-x/events-service";
import { createInteractiveEventModal, translations as modalTranslations } from "@sx-premium/interactive-event-modal";
import { createHourlyView, createDailyView, createConfig, translations as resourceViewTranslations, TimeUnits } from
"@sx-premium/resource-scheduler";
import { translations, mergeLocales } from '@schedule-x/translations'
import { signal } from "@preact/signals";

import '@sx-premium/resource-scheduler/index.css'
import '@sx-premium/interactive-event-modal/index.css'
import '@schedule-x/theme-default/dist/time-picker.css'

const resourceConfig = createConfig();
const hourlyView = createHourlyView(resourceConfig);
const dailyView = createDailyView(resourceConfig);

// enable or disable drag and drop, resizing
resourceConfig.resize.value = true
resourceConfig.dragAndDrop.value = true

// optionally set the initially displayed columns of the hourly view
// can be combined with `infiniteScroll.value = false` to achieve a scheduler with fixed columns
resourceConfig.initialHours.value = new TimeUnits().getDayHoursBetween(
  Temporal.PlainDateTime.from('2025-03-07T08:00'),
  Temporal.PlainDateTime.from('2025-03-07T19:00')
)

// optionally set the initially displayed days of the daily view
// can be combined with `infiniteScroll.value = false` to achieve a scheduler with fixed columns
resourceConfig.initialDays.value = new TimeUnits().getDaysBetween(
  Temporal.PlainDate.from('2025-03-05'),
  Temporal.PlainDate.from('2025-03-20')
)

resourceConfig.resources.value = [
  {
    label: 'Room 100',
    id: '1',
    resources: [
      {
        label: 'Room 100A',
        id: '1.1',
        isOpen: signal(false),
        resources: [
          {
            label: 'Room 100A1',
            id: '1.1.1',
            colorName: 'room-100A1',
            lightColors: {
              main: '#1c7df9',
              container: '#d2e7ff',
              onContainer: '#002859',
            },
          },
          {
            label: 'Room 100A2',
            id: '1.1.2',
          },
          {
            label: 'Room 100A3',
            id: '1.1.3',
          }
        ],
      },
      {
        label: 'Room 100B',
        id: '1.2',
        isOpen: signal(false),
        resources: [
          {
            label: 'Room 100B1',
            id: '1.2.1',
          },
          {
            label: 'Room 100B2',
            id: '1.2.2',
          },
          {
            label: 'Room 100B3',
            id: '1.2.3',
          }
        ]
      }
    ]
  },
  {
    labelHTML: '<span>Room <strong>101</strong></span>',
    id: '2',
    colorName: 'room-101',
    lightColors: {
      main: '#1c7df9',
      container: '#d2e7ff',
      onContainer: '#002859'
    },
    darkColors: {
      main: '#c0dfff',
      onContainer: '#dee6ff',
      container: '#426aa2'
    }
  }
]


const eventsService = createEventsServicePlugin()

const interactiveEventModal = createInteractiveEventModal({
  eventsService,

  onAddEvent: (event) => {
    console.log('Event added', event)
  },

  fields: {
    title: {},
    resourceId: {}
  }
})

const calendar = createCalendar({
  translations: mergeLocales(
    translations,
    modalTranslations,
    resourceViewTranslations
  ),
  events: [
    {
      id: 1,
      title: 'Event 1',
      start: Temporal.ZonedDateTime.from('2024-05-11T14:00:00+09:00[Asia/Tokyo]'),
      end: Temporal.ZonedDateTime.from('2024-05-11T17:00:00+09:00[Asia/Tokyo]'),
      resourceId: '1'
    },
    {
      id: 2,
      title: 'Event 2',
      start: Temporal.ZonedDateTime.from('2024-05-11T14:00:00+09:00[Asia/Tokyo]'),
      end: Temporal.ZonedDateTime.from('2024-05-11T16:00:00+09:00[Asia/Tokyo]'),
      resourceId: '2'
    }
  ],
  views: [hourlyView, dailyView],
  plugins: [
    eventsService,
    interactiveEventModal
  ]
})

calendar.render(document.getElementById('your-calendar-wrapper'))

```


## API

### ResourceSchedulerConfig

`Signal` is a reactive variable which holds a `value` property. This enables you to reactively
update the value of your config variables. So if you wish to override the default hourWidth of the hourly view,
you can do so by setting `config.hourWidth.value = 100`.

```ts
export type ResourceViewConfig = {
  // width of a column in the hourly view
  hourWidth: Signal<number>

  // width of a column in the daily view
  dayWidth: Signal<number>

  // list of resources you want to display
  resources: Signal<Resource[]>

  // height of a resource row
  resourceHeight: Signal<number>

  // height of an event
  eventHeight: Signal<number>

  // should drag and drop be enabled
  dragAndDrop: Signal<boolean>

  // should resizing be enabled
  resize: Signal<boolean>

  // can be used to set the day boundaries of the hourly view
  // start and end can be integers between 0 and 23, representing the hour of the day
  dayBoundaries: Signal<{ start: number, end: number }>

  // should infinite scroll be enabled
  infiniteScroll: Signal<boolean>

  // callback that runs when the user scrolls the hourly view
  onLazyLoadDate?: (dates: Temporal.PlainDate[]) => void

  // Can be used to debounce the lazy load date callback with any number of milliseconds. Defaults to 0.
  lazyLoadDateDebounce: Signal<number>

  // callback that runs when the user scrolls the daily view
  onLazyLoadMonth?: (dates: Temporal.PlainDate[]) => void

  // callback for detecting clicks in the hourly view
  onClickDateTime: Signal<(datetime: Temporal.ZonedDateTime, resourceId: string) => void>

  // callback for detecting double clicks in the hourly view
  onDoubleClickDateTime: Signal<(datetime: Temporal.ZonedDateTime, resourceId: string) => void>

  // callback for detecting clicks in the daily view
  onClickDate: Signal<(date: Temporal.PlainDate, resourceId: string) => void>

  // callback for detecting double clicks in the daily view
  onDoubleClickDate: Signal<(date: Temporal.PlainDate, resourceId: string) => void>

  // callback for detecting clicks on a resource
  onClickResource: Signal<(resourceId: string) => void>

  // Can be used to set the initially displayed hours of the hourly view
  initialHours: Signal<Temporal.PlainDateTime[] | undefined>

  // Can be used to set the initially displayed days of the daily view
  initialDays: Signal<Temporal.PlainDate[] | undefined>

  // Can be used to configure the snapping duration of drag & drop
  dragSnapping: Signal<15 | 30 | 60>

  // Can be used to configure the snapping duration of resizing
  resizeSnapping: Signal<15 | 30 | 60>

  // Dictates whether the current day should be highlighted in both daily and hourly view. Defaults to true.
  highlightToday: signal(false),

  // Can be used to configure the format of the day names in daily view. Defaults to 'short'. Disable day names by setting to false.
  dayNameFormat: Signal<false | 'short' | 'long' | 'narrow' | ((date: Temporal.PlainDate, locale: string) => string)>
}
```

### Resource

```ts
export type Resource = {
  label?: string
  labelHTML?: string
  id: string
  colorName?: string
  lightColors?: ColorDefinition
  darkColors?: ColorDefinition
  resources?: Resource[]
  isOpen?: Signal<boolean>
}
```

### ColorDefinition

```ts
export type ColorDefinition = {
  main: string
  container: string
  onContainer: string
}
```


## Changelog

See [changelog](/premium-changelog) page.

## Examples

These can be added on request. Please let us know if you need an example for a specific framework.

* React example: https://github.com/schedule-x/react-examples/blob/main/resource-scheduler/src/App.tsx
* Vue example: https://github.com/schedule-x/vue-examples/tree/main/resource-scheduler
* Angular example: https://github.com/schedule-x/angular-examples/tree/main/resource-scheduler
